package models

import (
	"devops/src/common"
	"fmt"
	"sort"

	"strings"

	"github.com/go-kit/log"
	"github.com/go-kit/log/level"
)

type StreePath struct {
	Id       int64  `json:"id"`
	Level    int    `json:"level"`
	Path     string `json:"path"`
	NodeName string `json:"node_name"`
}

//给StreePath绑定方法

// 调用连接，添加一条数据。添加时其实已经有了对象，才进行了插入
func (sp *StreePath) Addone() (int64, error) {
	rowAffected, err := DB["stree"].Insert(sp)
	return rowAffected, err
}

// 查询时是没有记录的。带参数查询一条记录的函数，level=3 and path=/0

// 示例2: 查询 level=2 且 path="/1" 的记录
// StreePathGet("level=? and path=?", 2, "/1")
func StreePathGet(where string, args ...interface{}) (*StreePath, error) {
	var obj StreePath
	//has为bool 类型
	has, err := DB["stree"].Where(where, args...).Get(&obj)
	if err != nil {
		return nil, err
	}
	if !has {
		return nil, nil
	}
	return &obj, nil

}

// 查询多条记录的函数,弄清楚这里为什么不用指针了。
func StreePathGetMany(where string, args ...interface{}) ([]StreePath, error) {
	var objs []StreePath
	err := DB["stree"].Where(where, args...).Find(&objs)
	if err != nil {
		return objs, err
	}

	return objs, nil

}

func StreePathQuery(req *common.NodeCommonReq, logger log.Logger) (res []string) {
	switch req.QueryType {
	case 1:

		//根据g查询所有p的列表，node=g query_type=1
		nodeG := &StreePath{
			Level:    1,
			Path:     "0",
			NodeName: req.Node,
		}
		dbG, err := nodeG.GetOne()
		if err != nil {
			level.Error(logger).Log("msg", "query g failed", "err", err, "path", req.Node)
			return
		}
		if dbG == nil {
			//说明要查询的g不存在
			return
		}
		pathP := fmt.Sprintf("/%d", dbG.Id)
		whereStr := "level=? and path=?"
		ps, err := StreePathGetMany(whereStr, 2, pathP)
		if err != nil {
			level.Error(logger).Log("msg", "query_ps_failed", "err", err, "path", req.Node)
			return
		}
		for _, i := range ps {
			res = append(res, i.NodeName)
		}
		sort.Strings(res)
		return res
	case 2:
		/*query_type 2  根據g 查詢gpa
		查g 再查p 最後查a
		*/
		nodeG := &StreePath{
			Level:    1,
			Path:     "0",
			NodeName: req.Node,
		}
		dbG, err := nodeG.GetOne()
		if err != nil {
			level.Error(logger).Log("msg", "query g failed", "err", err, "path", req.Node)
			return
		}
		if dbG == nil {
			return
		}
		pathP := fmt.Sprintf("/%d", dbG.Id)
		whereStr := "level=? and path=?"
		ps, err := StreePathGetMany(whereStr, 2, pathP)
		if err != nil {
			level.Error(logger).Log("msg", "query_ps_failed", "err", err, "path", req.Node)
			return
		}
		if len(ps) == 0 {
			return

		}
		for _, p := range ps {
			pathA := fmt.Sprintf("%s/%d", p.Path, p.Id)
			as, err := StreePathGetMany(whereStr, 3, pathA)
			if err != nil {
				continue
				//代表這個p 沒東西,開始遍歷下一個p

			}
			if len(as) == 0 {
				//説明該p 下面沒有a
				continue
			}
			for _, a := range as {
				fullPath := fmt.Sprintf("%s.%s.%s", dbG.NodeName, p.NodeName, a.NodeName)
				res = append(res, fullPath)

			}

		}
		sort.Strings(res)
		return
	case 3:
		/*
			給出gp  查詢a
		*/
		gps := strings.Split(req.Node, ".")
		g, p := gps[0], gps[1]
		nodeG := &StreePath{
			Level:    1,
			Path:     "0",
			NodeName: g,
		}
		dbG, err := nodeG.GetOne()
		if err != nil {
			level.Error(logger).Log("msg", "query g failed", "err", err, "path", req.Node)
			return
		}
		if dbG == nil {
			//说明要查询的g不存在
			return
		}
		//此逻辑走到这里代表g存在,那么就要继续查询p
		pathP := fmt.Sprintf("%d", dbG.Id)
		whereStr := "level=? and path=? and node_name=?"
		dbp, err := StreePathGet(whereStr, 2, pathP, p)
		if err != nil {
			level.Error(logger).Log("msg", "query p failed", "err", err, "path", req.Node)
			return
		}
		if dbp == nil {
			//说明要查询的p不存在
			return
		}
		pathA := fmt.Sprintf("%s/%d", pathP, dbp.Id)

		whereStr = "level=? and path=?"
		as, err := StreePathGetMany(whereStr, 3, pathA)
		if err != nil {
			level.Error(logger).Log("msg", "query as failed", "err", err, "path", req.Node)
			return
		}
		for _, a := range as {
			fullPath := fmt.Sprintf("%s.%s.%s", dbG.NodeName, dbp.NodeName, a.NodeName)
			res = append(res, fullPath)

		}
		sort.Strings(res)
		return
	case 4:
		//直接查询g.p.a 是否存在。
		gpas := strings.Split(req.Node, ".")
		g, p, a := gpas[0], gpas[1], gpas[2]
		nodeG := &StreePath{
			Level:    1,
			Path:     "0",
			NodeName: g,
		}
		dbG, err := nodeG.GetOne()
		if err != nil {
			level.Error(logger).Log("msg", "query g failed", "err", err, "path", req.Node)
			return
		}
		if dbG == nil {
			//说明g不存在
			return
		}
		pathP := fmt.Sprintf("/%d", dbG.Id)
		nodeP := &StreePath{
			Level:    2,
			Path:     pathP,
			NodeName: p,
		}
		dbP, err := nodeP.GetOne()
		if err != nil {
			level.Error(logger).Log("msg", "query_p_failed", "err", err, "path", req.Node)
			return
		}
		if dbP == nil {
			return
		}
		pathA := fmt.Sprintf("%s/%d", dbP.Path, dbP.Id)
		nodeA := &StreePath{
			Level:    3,
			Path:     pathA,
			NodeName: a,
		}
		dbA, err := nodeA.GetOne()
		if err != nil {
			level.Error(logger).Log("msg", "query_a failed", "err", err, "path", req.Node)
			return
		}
		if dbA == nil {
			return
		}
		res = append(res, req.Node)
		return

	}
	return

}

// 带参数删除多条记录函数
func StreePathDelMany(where string) (int64, error) {
	rawSql := fmt.Sprintf(`delete from stree_path where %s`, where)
	res, err := DB["stree"].Exec(rawSql)
	if err != nil {
		return 0, err
	}
	rowsAffected, err := res.RowsAffected()
	return rowsAffected, err
}
func StreePathDelete(req *common.NodeCommonReq, logger log.Logger) (delNum int64) {
	path := strings.Split(req.Node, ".")
	pLevel := len(path)
	//	  传入g，如果g下有p就不让删g
	nodeG := &StreePath{
		Level:    1,
		Path:     "0",
		NodeName: path[0],
	}
	dbG, err := nodeG.GetOne()
	if err != nil {
		level.Error(logger).Log("msg", "query_g_failed", "path", req.Node, "err", err)
		return
	}
	if dbG == nil {
		// 说明要删除的g不存在
		return
	}

	pathP := fmt.Sprintf("/%d", dbG.Id)
	switch pLevel {
	case 1:

		if req.ForceDelete {
			// var delNum int64
			delAwhereStr := fmt.Sprintf(`path like '%d/%%' and level = 3`, dbG.Id)
			delANum, err := StreePathDelMany(delAwhereStr)
			if err != nil {
				level.Error(logger).Log("msg", "del_a_failed", "path", req.Node, "err", err)
				return
			}
			level.Info(logger).Log("msg", "del_a_success", "path", req.Node, "del_num", delANum)
			delNum += delANum
			delPwhereStr := fmt.Sprintf(`path like '%d' and level = 2`, dbG.Id)
			delPNum, err := StreePathDelMany(delPwhereStr)
			if err != nil {
				level.Error(logger).Log("msg", "del_p_failed", "path", req.Node, "err", err)
				return

			}

			level.Info(logger).Log("msg", "del_pa_success", "path", req.Node, "num", delNum)
			delNum += delPNum
			_, err = dbG.DelOne()
			if err != nil {
				level.Error(logger).Log("msg", "del_g_failed", "path", req.Node, "err", err)
				return
			}
			level.Info(logger).Log("msg", "del_g_success", "path", req.Node)
			delNum += 1
			return
		}

		//	  传入g，如果g下有p就不让删g

		whereStr := "level=? and path=?"
		ps, err := StreePathGetMany(whereStr, 2, pathP)
		if err != nil {
			level.Error(logger).Log("msg", "query_ps_failed", "path", req.Node, "err", err)
			return
		}
		if len(ps) > 0 {
			level.Warn(logger).Log("msg", "del_g_reject", "path", req.Node, "reason", "g_has_ps", "ps_num", len(ps))
			return
		}
		delNum, err = dbG.DelOne()
		if err != nil {
			level.Error(logger).Log("msg", "del_g_failed", "path", req.Node, "err", err)
			return
		}
		level.Info(logger).Log("msg", "del_g_success", "path", req.Node)
		return
	case 2:
		// 传入g.p，如果p下有a就不让删p
		nodeP := &StreePath{
			Level:    2,
			Path:     pathP,
			NodeName: path[1],
		}
		dbP, err := nodeP.GetOne()
		if err != nil {
			level.Error(logger).Log("msg", "query_p_failed", "path", req.Node, "err", err)
			return
		}
		if dbP == nil {
			// 说明p不存在
			return
		}
		pathA := fmt.Sprintf("%s/%d", dbP.Path, dbP.Id)
		whereStr := "level=? and path=?"
		as, err := StreePathGetMany(whereStr, 3, pathA)
		if err != nil {
			level.Error(logger).Log("msg", "query_as_failed", "path", req.Node, "err", err)
			return
		}
		if len(as) > 0 {
			level.Warn(logger).Log("msg", "del_g_p_reject", "path", req.Node, "reason", "p_has_as", "as_num", len(as))
			return
		}
		delNum, err = dbP.DelOne()
		if err != nil {
			level.Error(logger).Log("msg", "del_p_failed", "path", req.Node, "err", err)
			return
		}
		level.Info(logger).Log("msg", "del_p_success", "path", req.Node)
		return
	case 3:
		nodeP := &StreePath{
			Level:    2,
			Path:     pathP,
			NodeName: path[1],
		}
		dbP, err := nodeP.GetOne()
		if err != nil {
			level.Error(logger).Log("msg", "query_p_failed", "path", req.Node, "err", err)
			return
		}
		if dbP == nil {
			// 说明p不存在
			return
		}
		pathA := fmt.Sprintf("%s/%d", dbP.Path, dbP.Id)
		whereStr := "level=? and path=? and node_name=?"
		dbA, err := StreePathGet(whereStr, 3, pathA, path[2])
		if err != nil {
			level.Error(logger).Log("msg", "query_a_failed", "path", req.Node, "err", err)
			return
		}
		if dbA == nil {
			return
		}
		delNum, err = dbA.DelOne()
		if err != nil {
			level.Error(logger).Log("msg", "del_a_failed", "path", req.Node, "err", err)
			return
		}
		level.Info(logger).Log("msg", "del_a_success", "path", req.Node)
		return
	}
	return
}

func StreePathAddOne(req *common.NodeCommonReq, logger log.Logger) error {
	res := strings.Split(req.Node, ".")
	//要求新增时必须为gpa  一起传进来
	if len(res) != 3 {
		level.Info(logger).Log("msg", "node_name is not valid", "path", req.Node)
		return fmt.Errorf("node_name is not valid:%v", req.Node)
	}
	g, p, a := res[0], res[1], res[2]

	nodeG := &StreePath{
		Level:    1,
		Path:     "0", // 修正拼写错误
		NodeName: g,
	}
	dbG, err := nodeG.GetOne()
	if err != nil {
		level.Error(logger).Log("msg", "failed to get node_name", "path", req.Node, "err", err)
		return err
	}

	switch dbG {
	case nil:
		//说明g 不存在，依次插入gpa  g 不存在那么pa 也不存在
		_, err := nodeG.Addone()
		if err != nil {
			level.Error(logger).Log("msg", "failed to add g", "path", req.Node, "err", err)
			return err
		}
		level.Info(logger).Log("msg", "add g success", "path", req.Node)
	default:
		//g 存在
		level.Info(logger).Log("msg", "g exist check p ", "path", req.Node)

		pathP := fmt.Sprintf("/%d", dbG.Id)
		nodeP := &StreePath{
			Level:    2,
			Path:     pathP,
			NodeName: p,
		}
		dbp, err := nodeP.GetOne()
		if err != nil {
			level.Error(logger).Log("msg", "get p failed", "path", req.Node, "err", err)
			return err
		}
		if dbp != nil {
			//说明dbp存在，那么就查a
			level.Info(logger).Log("msg", "g,p exist check a ", "path", req.Node)
			PathA := fmt.Sprintf("%s/%d", pathP, dbp.Id)
			nodeA := &StreePath{
				Level:    3,
				Path:     PathA,
				NodeName: a,
			}
			dbA, err := nodeA.GetOne()
			if err != nil {
				level.Error(logger).Log("msg", "get a failed", "path", req.Node, "err", err)
				return err
			}
			if dbA == nil {
				//说明dbA不存在，那么就加a
				_, err := nodeA.Addone()
				if err != nil {
					level.Error(logger).Log("msg", "add a failed", "path", req.Node, "err", err)
					return err

				}
				level.Info(logger).Log("msg", "add a success", "path", req.Node)
				return err

			}

			// if dbA!= nil{
			// 	level.Info(logger).Log("msg", "g,p,a exist ", "path", req.Node)
			// 	return
			// }
			//能走到这里，可以简写成如下
			level.Info(logger).Log("msg", "g,p,a exist ", "path", req.Node)
			return err

		}
		level.Info(logger).Log("msg", "g,p not exist ", "path", req.Node)
		// 能走到这里，那么说明p不存在，那么插入p  a
		_, err = nodeP.Addone()
		if err != nil {
			level.Error(logger).Log("msg", "add p failed", "path", req.Node, "err", err)
			return err
		}
		level.Info(logger).Log("msg", "add p success", "path", req.Node)
		// 能走到这里，那么说明p不存在，那么插入  a
		pathA := fmt.Sprintf("%s/%d", pathP, nodeP.Id)
		nodeA := &StreePath{
			Level:    3,
			NodeName: a,
			Path:     pathA,
		}
		_, err = nodeA.Addone()
		if err != nil {
			level.Error(logger).Log("msg", "g exist add a failed", "path", req.Node, "err", err)
			return err
		}
		level.Info(logger).Log("msg", "g exist add a success", "path", req.Node)

		_, err = nodeA.Addone()
		if err != nil {
			level.Error(logger).Log("msg", "add a failed", "path", req.Node, "err", err)
			return err
		}
		level.Info(logger).Log("msg", "add a success", "path", req.Node)
		return err

	}

	//开始增加p ,因为上述用了
	//level  从1开始，g-->1,p-->2,a-->3
	//path  g最特殊为0，其余要指向上层并且/id,如果是三层就是这个效果
	pathP := fmt.Sprintf("/%d", nodeG.Id)
	nodeP := &StreePath{
		Level:    2,
		Path:     pathP, // 修正拼写错误
		NodeName: p,
	}
	_, err = nodeP.Addone()
	if err != nil {
		level.Error(logger).Log("msg", "failed to add p", "path", req.Node, "err", err)
		return err
	}
	level.Info(logger).Log("msg", "add p success", "path", req.Node)
	pathA := fmt.Sprintf("%s/%d", pathP, nodeP.Id)
	nodeA := &StreePath{
		Level:    3,
		Path:     pathA,
		NodeName: a,
	}
	_, err = nodeA.Addone()
	if err != nil {
		level.Error(logger).Log("msg", "failed to add a", "path", req.Node, "err", err)
		return err
	}
	level.Info(logger).Log("msg", "add a success", "path", req.Node)
	return nil

}

//以上功能解释，一共三段式，split
//构造一个G--->getone G
//结果为nil,.....那么就插入流程
//不是nil,那么代表G存在，就要开始检查p,pathp=nodeg.id
//getone
//p ！=nil 那么就继续查a,pathA=pathp/dbp.id.....

// 根据部分条件查询一条记录
func (sp *StreePath) GetOne() (*StreePath, error) {
	has, err := DB["stree"].Get(sp)
	if err != nil {
		return nil, err
	}
	if !has {
		return nil, nil
	}
	return sp, nil
}

func (sp *StreePath) DelOne() (int64, error) {
	delNum, err := DB["stree"].Delete(sp)

	return delNum, err
}

// 检查一个记录是否存在,不会返回结果，只会返回一个bool
// 用这种形式，就是给sp 封装方法形式，必须要有个sp 对象
func (sp *StreePath) CheckExist() (bool, error) {
	exist, err := DB["stree"].Exist(sp)
	return exist, err
}

func StreePathTest(logger log.Logger) {
	ns := []string{
		"inf.monitor.thanos",
		"inf.monitor.kafka",
		"inf.monitor.prometheus",
		"inf.monitor.m3db",
		"inf.cicd.gray",
		"inf.cicd.deploy",
		"inf.cicd.jenkins",
		"waimai.qiangdan.queue",
		"waimai.qiangdan.worker",
		"waimai.ditu.kafka",
		"waimai.ditu.es",
		"waimai.qiangdan.es",
	}
	for _, n := range ns {
		req := &common.NodeCommonReq{
			Node: n,
		}
		StreePathAddOne(req, logger)
	}
}

// 根据node 查路径
func StreePathQueryTest1(logger log.Logger) {
	ns := []string{
		"inf",
		"a",
		"waimai",
	}
	for _, n := range ns {
		req := &common.NodeCommonReq{
			Node:      n,
			QueryType: 1,
		}
		res := StreePathQuery(req, logger)
		level.Info(logger).Log("msg", "stree path query result", "req.Node", n, "num", len(res), "details", strings.Join(res, ","))

	}
}

func StreePathQueryTest2(logger log.Logger) {
	ns := []string{
		"inf",
		"a",
		"waimai",
	}
	for _, n := range ns {
		req := &common.NodeCommonReq{
			Node:      n,
			QueryType: 2,
		}
		res := StreePathQuery(req, logger)
		level.Info(logger).Log("msg", "stree path query result", "req.Node", n, "num", len(res), "details", strings.Join(res, ","))

	}
}

func StreePathQueryTest3(logger log.Logger) {
	ns := []string{
		"inf.monitor",
		"a.b",
		"waimai.qiangdan",
	}
	for _, n := range ns {
		req := &common.NodeCommonReq{
			Node:      n,
			QueryType: 3,
		}
		res := StreePathQuery(req, logger)
		level.Info(logger).Log("msg", "stree path query result", "req.Node", n, "num", len(res), "details", strings.Join(res, ","))

	}
}
func StreePathDelTest(logger log.Logger) {
	ns := []string{
		"a",
		"b",
		"waimai",
		"waimai.cicd",
	}
	for _, n := range ns {
		req := &common.NodeCommonReq{
			Node: n,
		}
		res := StreePathDelete(req, logger)
		level.Info(logger).Log("msg", "stree path delete result", "req.Node", n, "del_num", res)
	}
}
